package yinshi.jlex;

import java.io.IOException;
import java.util.regex.Pattern;
import yinshi.fsm.TextParser;
import yinshi.fsm.TextParser.Condition;

public final class JavaLexer {

    private static final String[] keywords = {"class", "final", "true", "false", "null"};
    private static final String[] operators = {">>>=", ">>>", "<<=", ">>=", "==", "<<", "<=", ">>",
        ">=", "!=", "&&", "||", "++", "--", "-=", "+=", "*=", "/=", "&=",
        "|=", "^=", "%=", "=", ">", "<", "!", "~", "?", ":", "+", "-", "*", "/", "&", "|", "^", "%"};

    private final TextParser parser = new TextParser(10);
    private CharSequence text;
    private int offset;
    private int lineNumber = 1;

    public JavaLexer(CharSequence text, int offset) {
        this.text = text;
        this.offset = offset;
        init();
    }

    private void init() {
        char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

        TextParser.State state = parser.getState(0);
        state.addJump(0, Pattern.compile("[ \t\f]+"), false, null);
        state.addJump(0, new String[]{"\n", "\r\n", "\r"}, false, null);
        state.addJump(0, Pattern.compile("//.*"), false, null);
        state.addJump(0, new CommentCond(), false, null);
        state.addJump(1, ".", false, null);
        state.addJump(2, digits, false, null);
        state.addJump(3, "\"", false, null);
        state.addJump(4, "\'", false, null);
        state.addJump(0, new char[]{'(', ')', '{', '}', '[', ']', ';', ','}, false, null);
        state.addJump(0, operators, false, null);
        state.addJump(0, keywords, false, null);
        state.addJump(0, new IdentifierCond(), false, null);

        state = parser.getState(1);
        state.addJump(2, digits, true, null);
        state.setDefaultTarget(0);

        state = parser.getState(2);
        state.addJump(2, digits, false, null);
        state.setDefaultTarget(0);

        state = parser.getState(3);
        state.addJump(3, Pattern.compile("[^\"]+"), false, null);
        state.addJump(0, '"', false, null);

        state = parser.getState(4);
        state.addJump(4, Pattern.compile("[^']*"), false, null);
        state.addJump(0, '\'', false, null);
    }

    public void process() throws IOException {
        parser.process(text, offset);
    }

    private static class IdentifierCond implements TextParser.Condition {

        public int judge(int stateId, int jumpIndex, CharSequence text, TextParser.Result reObj) {
            char ch = text.charAt(0);
            if (!Character.isJavaIdentifierStart(ch)) {
                return TextParser.FALSE_JUDGE;
            }

            StringBuilder sb = new StringBuilder();
            sb.append(ch);
            for (int i = 1; i < text.length(); i++) {
                if (!Character.isJavaIdentifierPart(text.charAt(i))) {
                    reObj.data = sb.toString();
                    return i;
                }
                sb.append(ch);
            }
            reObj.data = sb.toString();
            return text.length();
        }
    }

    private static class CommentCond implements TextParser.Condition {

        public int judge(int stateId, int jumpIndex, CharSequence text, TextParser.Result reObj) {
            if ((text.charAt(0) == '/') && (text.length() > 1) && (text.charAt(1) == '*')) {
                if (text.length() < 4) {
                    return text.length();
                }
                for (int i = 2; i < text.length() - 1; i++) {
                    if ((text.charAt(i) == '*') && (text.charAt(i + 1) == '/')) {
                        return i + 2;
                    }
                }
                return text.length();
            }
            return TextParser.FALSE_JUDGE;
        }
    }

    private class Hand_0 implements TextParser.Handler {

        public void hit(int stateId, int targetId, int jumpIndex, TextParser.Result reObj) {
            switch (jumpIndex) {
                case 0:
                    System.out.print(reObj.data);
            }
        }
    }
}
